local Item = require("bag.Item")
local Equip = require("bag.Equip")
local ItemDefine = require("bag.ItemDefine")
local RoleFFI = require("role.RoleFFI")
local EquipExcel = require("excel.Equip")
local GemExcel = require("excel.Gem")
local GemConf = GemExcel.gem
local StrengthConfig = require("excel.Strength")
local EquipQulity = EquipExcel.equipQulity
local ExcelItem = require("excel.Item")
local Msg = require("core.Msg")

function create(dst, id, cnt, bind, qualityOut, lifeTime, strengthLv)
    local grid = dst or {}
    grid.id = id
    grid.cnt = cnt
    grid.uuid = _G.gen_oid() or ""
    grid.createTime = os.time()
    if not bind or bind  == 0 then
        grid.bind = 0
    else
        grid.bind = 1
    end

    if not lifeTime then
        local nLiftTime = ItemDefine.getValue(id, "lifeTime")
        if nLiftTime and nLiftTime > 0 then
            lifeTime = nLiftTime + os.time()
        end
    end

    grid.lifeTime = lifeTime or 0
    if not grid.data then
        grid.data = {}
    end

    if ItemDefine.isItem(id) then
        if not grid.data.item then
            grid.data.item = {}
        end
        Item.create(grid.data.item, id)
    elseif ItemDefine.isEquip(id) then
        if not grid.data.equip then
            grid.data.equip = {}
        end
        Equip.create(grid.data.equip, id, qualityOut, strengthLv, grid)
    elseif ItemDefine.isGem(id) then
        --do nothing
    else
        assert(nil, id)
    end
    return grid
end

function copy2(bag, dst_index, src_index)
    RoleFFI.copy_array_element(bag, dst_index, src_index, 'struct grid')
end

function copy(dst, src, src_is_ffi)
    if not src or not dst then
        return
    end
    dst.id = src.id
    dst.cnt = src.cnt
    dst.bind = src.bind
    if src_is_ffi then
        dst.uuid = RoleFFI.ffi_string(src.uuid)
    else
        dst.uuid = src.uuid
    end
    dst.lifeTime = src.lifeTime
    dst.createTime = src.createTime
    dst.open = src.open or 0
    if dst.data == nil then
        dst.data = {}
    end
    if src.id == 0 then
        return
    elseif ItemDefine.isItem(src.id) then
        if src.data.item then
            if dst.data.item == nil then dst.data.item = {} end
            Item.copy(dst.data.item, src.data.item)
        end
    elseif ItemDefine.isGem(src.id) then
        if src.data.item then
            if dst.data.item == nil then dst.data.item = {} end
            Item.copy(dst.data.item, src.data.item)
        end
    elseif ItemDefine.isEquip(src.id) then
        if dst.data.equip == nil then dst.data.equip = {} end
        Equip.copy(dst.data.equip, src.data.equip)
    else
        assert(nil, src.id)
    end
end


function makeItem(grid, itemData, index, human)
    if grid and grid.id ~= 0 then
        itemData.id = grid.id
        itemData.index = index or 0
        itemData.bind = grid.bind
        itemData.cnt = grid.cnt
        local now = os.time()
        local lifeTime = 0
        if grid.lifeTime ~= 0 then
            lifeTime = grid.lifeTime - now
        end
        itemData.lifeTime = lifeTime
        itemData.icon = ItemDefine.getValue(grid.id, "icon")
        itemData.price = ItemDefine.getPrice(grid)
        itemData.name = ItemDefine.getValue(grid.id, "name")
        itemData.color = ItemDefine.getValue(grid.id, "color")
        itemData.subType1 = ItemDefine.getValue(grid.id, "subType1")
        itemData.subType2 = ItemDefine.getValue(grid.id, "subType2")
        itemData.open = grid.open or 1
        itemData.showUseDesc = ItemDefine.getValue(grid.id, "showUseDesc") or 0
        itemData.canSearch=ItemDefine.getValue(grid.id, "canSearch") or 0
        if ItemDefine.isEquip(grid.id) then
            itemData.lv = Equip.getPutOnLv(grid)
        else
            itemData.lv = ItemDefine.getValue(grid.id, "lv")
        end

        --item
        itemData.items[0] = 0
        -- equip
        itemData.equips[0] = 0
        -- gem
        itemData.gems[0] = 0

        if ItemDefine.isItem(grid.id) then     --item
            itemData.items[0] = 1
            local item = itemData.items[1]
            item.coolGroup = ItemDefine.getValue(grid.id, "coolGroup")
            item.coolTime = ItemDefine.getValue(grid.id, "coolTime")
            item.leftCoolTime = 0

            if item.coolGroup ~= 0 and item.coolTime ~= 0 and human then
                local nCoolData = human.db.item_use_cool
                if nCoolData then
                    for i = 0, human.db.item_use_coolLen - 1 do
                        if nCoolData[i].group == item.coolGroup then
                            local nRemainCool = now - nCoolData[i].usetime
                            item.leftCoolTime = nRemainCool < item.coolTime and item.coolTime - nRemainCool or 0
                            break
                        end
                    end
                end
            end
            item.values[0] = 0
            item.values_type = 0
            if human then
                local command = ItemDefine.getValue(grid.id, "command")
                if command and command[1] == "attr" then
                    item.values_type = 1
                    item.values[0] = 1
                    item.values[1] = 0
                    for i = 0, human.db.attr_itemLen - 1 do
                        if human.db.attr_item[i].itemid == grid.id then
                            item.values[1] = human.db.attr_item[i].cnt
                            break
                        end
                    end
                end
            end
            item.autoUse = ItemDefine.getValue(grid.id, "autoUse")
            item.canAppend = ItemDefine.getValue(grid.id, "canAppend")

            item.sex = ItemDefine.getValue(grid.id, "sex")

            item.score = ItemDefine.getValue(grid.id, "score")
        elseif ItemDefine.isEquip(grid.id) then -- equip
            local equip = grid.data.equip
            itemData.equips[0] = 1
            local equipData = itemData.equips[1]
            equipData.pos = ItemDefine.getValue(grid.id, "pos")
            local baseAttr = equipData.baseAttr
            baseAttr[0] = 0
            local otherAttr = equipData.otherAttr
            otherAttr[0] = 0

            itemData.color = Equip.getColor(equip.quality)
            equipData.sex =  ItemDefine.getValue(grid.id, "sex") or 0

            equipData.quality = equip.quality
            itemData.canSearch = 0
            equipData.callSkillID = 0 --ItemDefine.getValue(grid.id, "callSkillID")
            equipData.dressType = ItemDefine.getValue(grid.id, "dressType") or 0
            equipData.strengthLv = equip.strengthLv
            if equip.strengthLv > 0 then
                local strengthenID = EquipExcel.equip[grid.id].strengthenID
                equipData.strengthAttr[0] = #StrengthConfig[strengthenID][equip.strengthLv].addAttrs
                for i=1, #StrengthConfig[strengthenID][equip.strengthLv].addAttrs do
                    equipData.strengthAttr[i].key = StrengthConfig[strengthenID][equip.strengthLv].addAttrs[i][1]
                    equipData.strengthAttr[i].value = StrengthConfig[strengthenID][equip.strengthLv].addAttrs[i][2]
                end
            else
                equipData.strengthAttr[0] = 0
            end
            equipData.xiLianAttr[0] = 4
            for i=0, 3 do
                equipData.xiLianAttr[i+1].key = equip.xiLianAttr[i]
                equipData.xiLianAttr[i+1].value = equip.xiLianAttrVal[i]
                equipData.xiLianAttr[i+1].star = equip.xiLianStar[i]
            end
            -- 基础属性
            for k, v in ipairs(EquipExcel.equip[grid.id].baseAttr) do
                baseAttr[0] = baseAttr[0] + 1
                local attr = baseAttr[baseAttr[0]]
                attr.key = v[1]                
                if EquipExcel.equip[grid.id].dressType ~= 1 then                  
                  attr.value = v[2] 
                else
                  attr.value = v[2] + v[2] * EquipQulity[equip.quality].attrAddRate
                end
                attr.other = 0
                attr.tip = ""
            end
            --其他基础属性
            for k, v in ipairs(EquipExcel.equip[grid.id].baseOtherAttr) do
                otherAttr[0] = otherAttr[0] + 1
                local attr = otherAttr[otherAttr[0]]
                attr.key = v[1]
                attr.value = v[2]
            end

            for i = 2, baseAttr[0] do
                for j = 1, i - 1 do
                    if baseAttr[i].key < baseAttr[j].key then
                        baseAttr[i], baseAttr[j] = baseAttr[j], baseAttr[i]
                    end
                end
            end
            equipData.xiLianPower = equip.xiLianPower
            local source = ItemDefine.getValue(grid.id, "source")
            equipData.source = source and source[1] or 0
            equipData.hole = equip.hole
            -- gem
            local gem = equipData.gem
            local len = 0
            for i =0, equip.hole - 1 do
                len = len + 1
                local g = gem[len]
                g.k = i
                g.id = equip.gems[i]
                local gemConfig = GemConf[g.id]
                g.gemAttr[0] = 0
                if gemConfig then
                    for key, value in pairs(gemConfig.attr) do
                        g.gemAttr[0] = g.gemAttr[0] + 1
                        local gemAttr = g.gemAttr[g.gemAttr[0]]
                        gemAttr.key = value[1]
                        gemAttr.value = value[2]  -- * (1 + gemAttrUpRate)
                    end
                    g.color = gemConfig.color
                    g.lv = gemConfig.lv
                    g.name = gemConfig.name
                    g.icon = gemConfig.icon
                    g.price = gemConfig.price
                    g.type = gemConfig.hole
                else
                    g.color = 0
                    g.lv = 0
                    g.name = ""
                    g.icon = 0
                    g.price = 0
                    g.exp = 0
                    g.expMax = 0
                    g.canUp = 0
                    g.attrPoint = 0
                    g.type = 0
                end
            end
            gem[0] = len
            -- 评分
            equipData.score = math.floor(Equip.getZhandouli(human, grid))
        elseif ItemDefine.isGem(grid.id) then --gem
            local gemConfig = GemConf[grid.id]
            for k, v in ipairs(gemConfig.attr) do
                itemData.gems[0] = itemData.gems[0] + 1
                local attr = itemData.gems[itemData.gems[0]]
                attr.key = v[1]
                attr.value = v[2]
                attr.type = gemConfig.hole
            end
        else
            print("id ="..grid.id)
            assert()
        end
    else
        itemData.id = 0
        itemData.index = index
        itemData.bind = 0
        itemData.cnt = 0
        itemData.lifeTime = 0
        itemData.icon = 0
        itemData.price = 0
        itemData.name = ""
        itemData.lv = 0
        itemData.color = 0
        itemData.subType1 = 0
        itemData.subType2 = 0
        itemData.canSearch = 0
        itemData.items[0] = 0
        itemData.gems[0] = 0
        itemData.equips[0] = 0
        itemData.open = 0
        itemData.showUseDesc = 0
    end
end

local tb = {}
local bag = {}
function sort(bagg, bagLen)
    local len = 0

    for i = 0, bagLen - 1 do
        if bagg[i] then
            len = len + 1
            bag[len] = {}
            copy(bag[len], bagg[i], true)
        end
    end

    local realLen = len
    for i = realLen, #bag do
        bag[i + 1] = nil
    end
    len = 0

    for i = 0, realLen - 1 do
        if bag[i+1].id ~= 0 then
            len = len + 1
            tb[len] = bag[i+1]
        end
    end
    for i = len + 1, #tb do
        tb[i] = nil
    end
    table.sort(tb, cmp)
    for i = 1, realLen  do
        bag[i] = tb[i]
    end

    merge(bag)

    local len = #bag
    for i = 1, len do
        local grid = bag[i]
        if grid.cnt < 1 then
            grid.id = 0
        end
    end

    for i = 0, len - 1 do
        bagg[i] = bagg[i] or {}
        copy(bagg[i], bag[i + 1])
    end
    for i = len, bagLen - 1 do
        if bagg[i] then
            bagg[i].id = 0
        end
    end
end

function merge(bag)
    local i = 2
    local j = 1
    while i <= #bag do
        if i <= j or bag[i].cnt < 1 then
            i = i + 1
        elseif transfer(bag[i], bag[j], ItemDefine.CAP_PER_GRID) < 1 then
            j = j + 1
        elseif bag[i] == bag[j] then
            assert(false)
        end
    end
end

function transfer(src, dst, cnt)
    if not cnt or cnt < 1 then
        return 0
    end
    if src.cnt < 1 then
        return 0
    end

    if dst.cnt > 0 and (src.id ~= dst.id or src.bind ~= dst.bind or src.lifeTime ~= dst.lifeTime or src.open ~= dst.open or not ItemDefine.canAppend(dst.id)) then
        return 0
    end
    if dst.cnt < 1 then
        dst.id = src.id
        dst.bind = src.bind
        dst.lifeTime = src.lifeTime
        dst.item, src.item = src.item, dst.item
        dst.equip, src.equip = src.equip, dst.equip
        dst.gem, src.gem = src.gem, dst.gem
        dst.open = src.open
    end
    local cntReal = math.min(src.cnt, ItemDefine.CAP_PER_GRID - dst.cnt)
    cntReal = math.min(cntReal, cnt)
    dst.cnt = dst.cnt + cntReal
    src.cnt = src.cnt - cntReal
    return cntReal
end

-- 返回true排先

local function get_type(item_id)
    if EquipExcel.equip[item_id] then
        return 1
    end
    if GemExcel.gem[item_id] then
        return 2
    end
    local subType1 = ExcelItem.item[item_id].subType1
    if subType1 == 9 or subType1 == 10 then
        return 3
    end
    return 4
end

function cmp(a, b)

    local type_a = get_type(a.id)
    local type_b = get_type(b.id)
    if type_a ~= type_b then
        return type_a < type_b
    end

    local cf_a = ItemDefine.getCf(a.id)
    local cf_b = ItemDefine.getCf(b.id)

    if EquipExcel.equip[a.id] then
        local aColor = a.data.equip.quality
        local bColor = b.data.equip.quality
        if aColor ~= bColor then
            return aColor > bColor
        end

        local poa_a = cf_a.pos
        local pob_b = cf_b.pos
        if poa_a ~= pob_b then
            return poa_a < pob_b
        end
    else
        local sub1_a = cf_a.subType1
        local sub1_b = cf_b.subType1
        if sub1_a ~= sub1_b then
            return sub1_a < sub1_b
        end

        local sub2_a = cf_a.subType2
        local sub2_b = cf_b.subType2
        if sub2_a ~= sub2_b then
            return sub2_a < sub2_b
        end

        local aColor = cf_a.color
        local bColor = cf_b.color
        if aColor ~= bColor then
            return aColor > bColor
        end
    end

    if a.id ~= b.id then
        return a.id < b.id
    end

    if a.bind ~= b.bind then
        return a.bind == 1
    end

    if a.cnt ~= b.cnt then
        return a.cnt > b.cnt
    end

    if a.open ~= b.open then
        return (a.open or 0) > (b.open or 0)
    end
end

local bagIndex = {}
function makeList(human, grids, cap, msgList, msgClear, fds)

    -- clear
    if msgClear then
        if not fds then
            Msg.send(msgClear, human.fd)
        else
            Msg.sendMulti(msgClear, fds)
        end
    end

    local len = 0
    for index = 0, cap-1 do
        if grids[index] and grids[index].id ~= 0 then
            len = len + 1
            bagIndex[len] = index
        end
    end

    if not msgClear and len < 1 then
        return
    end

    if len <= ItemDefine.LIST_PER_CNT then
        makeGrids(grids, bagIndex, 1, len, msgList.itemDatas, human)
        if not fds then
            Msg.send(msgList, human.fd)
        else
            Msg.sendMulti(msgList, fds)
        end
    else
        local cnt = math.ceil(len / ItemDefine.LIST_PER_CNT)
        for i = 1, cnt do
            makeGrids(grids, bagIndex, (i - 1) * ItemDefine.LIST_PER_CNT + 1,  math.min(ItemDefine.LIST_PER_CNT, len - (i - 1) * ItemDefine.LIST_PER_CNT), msgList.itemDatas, human)
            if not fds then
                Msg.send(msgList, human.fd)
            else
                Msg.sendMulti(msgList, fds)
            end
        end
    end
end

function makeGrids(grids, indexs, start, len, itemDatas, human)
    itemDatas[0] = 0
    local len = math.min(len, #indexs)
    for i = start, start + len - 1 do
        itemDatas[0] = itemDatas[0]  + 1
        makeItem(grids[indexs[i]], itemDatas[itemDatas[0]], indexs[i], human)
    end
end

